package com.udp.nml.example;

import java.net.InetSocketAddress;
import java.net.SocketAddress;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.nio.NioDatagramConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Example UDP Client implementing Apache Mina IoHandler
 * 
 * @author Niall McLoughlin
 */
public class UDPClientSpecifyingPort implements IoHandler
{
	static Logger logger = LoggerFactory.getLogger(UDPClientSpecifyingPort.class);
	
	NioDatagramConnector connector;
	
	IoSession session;
	int port;
	
	public static void main(String[] args)
	{
		UDPClientSpecifyingPort client = new UDPClientSpecifyingPort(UDPPortSpecifyingServer.DEFAULT_PORT);
		client.connect();
	}
	
	public UDPClientSpecifyingPort(int port)
	{
		this.port = port;
	}
	
	public void connect()
	{
		logger.info(">>> connect");
		
		connector = new NioDatagramConnector();
		connector.setHandler( this );
		ConnectFuture connFuture = connector.connect( new InetSocketAddress("localhost", port));
		
		connFuture.addListener( new IoFutureListener(){
            public void operationComplete(IoFuture future) 
            {
                ConnectFuture connFuture = (ConnectFuture)future;
                
                if(connFuture.isConnected())
                {
                    session = future.getSession();
                    try 
                    {
                        sendData();
                    } 
                    catch (InterruptedException e) 
                    {
                        e.printStackTrace();
                    }
                } 
                else 
                {
                	logger.info("not connected");
                }
            }
        });
		
		logger.info("<<< connect");
	}
	
	/**
	 * Send some data.
	 */
	private void sendData() throws InterruptedException 
	{
		logger.info(">>> sendData");
		
		Packet packet = new Packet();
		packet.setType(PacketType.UNIT_ID.getByteValue());
		packet.setSerialNumber('T');	
		
		packet.setData("INITIAL".getBytes());
		byte[] data = packet.getData();
		
		String localAddress = session.getLocalAddress().toString();
		long creationTime = session.getCreationTime();
		String message = "Message from " + localAddress + " at " + creationTime;
		
		logger.info("Initial Packet: " + packet);
		UDPUtils.sendBytesOnSession(session, packet.getBytes());
        
        //connector.dispose();
		
		logger.info("<<< sendData");
	}
	
	@Override
	public void exceptionCaught(IoSession arg0, Throwable arg1)
			throws Exception 
	{
		logger.info(">>> exceptionCaught");
		
		logger.info("<<< exceptionCaught");
	}
	@Override
	public void messageReceived(IoSession session, Object message) throws Exception
	{
		logger.info(">>> messageReceived");
		
		if (message instanceof IoBuffer) 
        {
            // Cast the message to an IoBuffer. 
			// The basic DatagramSocket messages arrive as an IoBuffer and the 
			// Apache Mina version does too.
			IoBuffer buffer = (IoBuffer) message;
			
			Packet packet = new Packet(buffer);
			
			// Get the address of the remote client.
            SocketAddress remoteAddress = session.getRemoteAddress();

            StringBuffer logMessage = new StringBuffer();
            logMessage.append("Remote Address:");
            logMessage.append(remoteAddress);
            logMessage.append("Incoming Packet:");
            logMessage.append(packet);
            
            // Log the message.
            logger.info(logMessage.toString());
            
            handleIncomingPacket(session, packet);
        } 
		
		logger.info("<<< messageReceived");
	}
	
	private void handleIncomingPacket(IoSession session, Packet packet)
	{
		logger.info(">>> sendAcknowledgement");
		
		PacketType incomingPacketType = PacketType.getType(packet.getType());
		
		if (PacketType.REG_DATA.equals(incomingPacketType))
		{
			Packet returnPacket = new Packet();
			returnPacket.setType(PacketType.GOT_REG.getByteValue());
			returnPacket.setSerialNumber(packet.getSerialNumber());
			returnPacket.setData("PAYLOAD".getBytes());
			
			logger.info("Sending Response: " + returnPacket);

			byte[] responseAsBytes = returnPacket.getBytes();
			UDPUtils.sendBytesOnSession(session, responseAsBytes);
		}
        
        logger.info("<<< sendAcknowledgement");
	}
	
	@Override
	public void messageSent(IoSession arg0, Object arg1) throws Exception 
	{
		logger.info(">>> messageSent");
		
		logger.info("<<< messageSent");
	}
	
	@Override
	public void sessionClosed(IoSession arg0) throws Exception
	{
		logger.info(">>> sessionClosed");
		
		logger.info("<<< sessionClosed");
	}
	
	@Override
	public void sessionCreated(IoSession arg0) throws Exception 
	{
		logger.info(">>> sessionCreated");
		
		logger.info("<<< sessionCreated");		
	}
	@Override
	public void sessionIdle(IoSession arg0, IdleStatus arg1) throws Exception 
	{
		logger.info(">>> sessionIdle");
		
		logger.info("<<< sessionIdle");
	}
	
	@Override
	public void sessionOpened(IoSession arg0) throws Exception 
	{
		logger.info(">>> sessionOpened");
		
		logger.info("<<< sessionOpened");		
	}
}
